#include "acl_stdafx.hpp"
#include <string.h>
#include <stdio.h>
#include "acl_cpp/stdlib/string.hpp"
#include "acl_cpp/stdlib/log.hpp"
#include "acl_cpp/stdlib/escape.hpp"
#include "acl_cpp/hsocket/hsrow.hpp"
#include "acl_cpp/hsocket/hserror.hpp"
#include "acl_cpp/hsocket/hsproto.hpp"

namespace acl
{

#define	MAX_INT	2147483647

	hsproto::hsproto(bool cache_enable)
	: debugOn_(false)
	, cache_enable_(cache_enable)
	, buf_ptr_(NULL)
	{
	}

	hsproto::~hsproto()
	{
		reset();
		clear_cache();
	}

	bool hsproto::build_open(string& out, int id,
		const char* dbn, const char* tbl,
		const char* idx, const char* flds)
	{
		out.clear();
		out << "P\t" << id << '\t' << dbn << '\t' << tbl
			<< '\t' << idx << '\t' << flds << '\n';
		return (true);
	}

	bool hsproto::build_get(string& out, int id, const char* values[], int num,
		const char* cond /* = "=" */, int nlimit /* = 0 */, int noffset /* = 0 */)
	{
		if (nlimit <= 0)
			nlimit = MAX_INT;
		if (noffset < 0)
			noffset = 0;
		char buf[32], *limit_offset = NULL;
		if (nlimit > 1)
		{
			snprintf(buf, sizeof(buf), "%d\t%d", nlimit, noffset);
			limit_offset = buf;
		}

		build_request(out, id, cond, values, num, limit_offset, 0, 0, 0);
		return (true);
	}

	bool hsproto::build_get(string& out, int id, int nfld,
		const char* first_value, ...)
	{
		va_list ap;
		char* ptr;
		char** pptr;

		pptr = (char**) acl_mycalloc((size_t) nfld, sizeof(char*));
		va_start(ap, first_value);

		pptr[0] = (char*) first_value;
		int   i = 1;
		while ((ptr = va_arg(ap, char*)) != NULL)
		{
			if (i >= nfld)
				break;
			pptr[i] = ptr;
			i++;
		}
		va_end(ap);

		bool ret = build_get(out, id, (const char**) pptr, i, "=", 0, 0);
		acl_myfree(pptr);
		return (ret);
	}

	bool hsproto::build_mod(string& out, int id, const char* values[], int num,
		const char* to_values[], int to_num, const char* cond /* = "=" */,
		int nlimit /* = 0 */, int noffset /* = 0 */)
	{
		if (nlimit <= 0)
			nlimit = MAX_INT;
		if (noffset < 0)
			noffset = 0;
		char buf[32], *limit_offset = NULL;

		snprintf(buf, sizeof(buf), "%d\t%d", nlimit, noffset);
		build_request(out, id, cond, values,
			num, limit_offset, 'U', to_values, to_num);
		return (true);
	}

	bool hsproto::build_del(string& out, int id, const char* values[],
		int num, const char* cond /* = "=" */,
		int nlimit /* = 0 */, int noffset /* = 0 */)
	{
		if (nlimit <= 0)
			nlimit = 1;
		if (noffset < 0)
			noffset = 0;
		char buf[32], *limit_offset = NULL;
		if (nlimit > 1)
		{
			snprintf(buf, sizeof(buf), "%d\t%d", nlimit, noffset);
			limit_offset = buf;
		}

		build_request(out, id, cond, values, num, limit_offset, 'D', 0, 0);
		return (true);
	}

	bool hsproto::build_del(string& out, int id, int nfld, const char* first_value, ...)
	{
		va_list ap;
		char* ptr;
		char** pptr;

		pptr = (char**) acl_mycalloc((size_t) nfld, sizeof(char*));
		va_start(ap, first_value);

		pptr[0] = (char*) first_value;
		int   i = 1;
		while ((ptr = va_arg(ap, char*)) != NULL)
		{
			if (i >= nfld)
				break;
			pptr[i] = ptr;
			i++;
		}
		va_end(ap);

		bool ret = build_del(out, id, (const char**) pptr, i, "=", 0, 0);
		acl_myfree(pptr);
		return (ret);

	}

	bool hsproto::build_add(string& out, int id, const char* values[], int num)
	{
		build_request(out, id, "+", values, num, 0, 0, 0, 0);
		return (true);
	}

	bool hsproto::build_add(string& out, int id, int nfld, const char* first_value, ...)
	{
		va_list ap;
		char* ptr;
		char** pptr;

		pptr = (char**) acl_mycalloc((size_t) nfld, sizeof(char*));
		va_start(ap, first_value);

		pptr[0] = (char*) first_value;
		int   i = 1;
		while ((ptr = va_arg(ap, char*)) != NULL)
		{
			if (i >= nfld)
				break;
			pptr[i] = ptr;
			i++;
		}
		va_end(ap);

		bool ret = build_add(out, id, (const char**) pptr, i);
		acl_myfree(pptr);
		return (ret);
	}

	void hsproto::build_request(string& buf, int id, const char* oper,
		const char* values[], int num,
		const char* limit_offset, char mop,
		const char* to_values[], int to_num)
	{
		char  idbuf[32], numbuf[32];

		buf.clear();

		snprintf(idbuf, sizeof(idbuf), "%d", id);
		snprintf(numbuf, sizeof(numbuf), "%d", num);
		buf << idbuf << "\t" << oper << "\t" << numbuf;

		int  i;
		for (i = 0; i < num; i++)
		{
			buf << "\t";
			escape(values[i], strlen(values[i]), buf);
		}

		if (limit_offset)
			buf << "\t" << limit_offset;

		if (mop)
			buf << "\t" << mop;

		if (to_values && to_num > 0)
		{
			for (i = 0; i < to_num; i++)
			{
				buf << "\t";
				escape(to_values[i], strlen(to_values[i]), buf);
			}
		}

		buf << "\n";
	}

	static const char* dummy_ok = "ok";

	bool hsproto::parse_respond(int nfld, string& buf,
		int& errnum, const char*& serror)
	{
		serror = dummy_ok;

		if (buf.empty())
		{
			errnum = HS_ERR_EMPTY;
			logger_error("respond empty");
			return (false);
		}

		buf_ptr_ = buf.c_str();
		char *last = buf_ptr_, *save = NULL;
		while (*buf_ptr_)
		{
			if (*buf_ptr_ == '\t')
			{
				save = buf_ptr_;
				*buf_ptr_++ = 0;
				break;
			}
			buf_ptr_++;
		}
		errnum = atoi(last);
		if (*buf_ptr_ == 0)
		{
			if (save)
				*save = '\t';
			errnum = HS_ERR_INVALID_REPLY;
			logger_error("respond(%s) invalid", buf.c_str());
			return (false);
		}

		last = buf_ptr_;
		save = NULL;
		while (*buf_ptr_)
		{
			if (*buf_ptr_ == '\t')
			{
				save = buf_ptr_;
				*buf_ptr_++ = 0;
				break;
			}
			buf_ptr_++;
		}

		ntoken_ = atoi(last);
		if (ntoken_ <= 0)
		{
			if (errnum != 0)
			{
				errnum = HS_ERR_INVALID_REPLY;
				logger_error("ntoken(%d) invalid", ntoken_);
				return (false);
			}
			return (false);
		}

		if (ntoken_ > nfld)
			ntoken_ = nfld;

		if (errnum == 0)
			return (true);

		serror = buf_ptr_;
		return (false);
	}

	hsrow* hsproto::get_next_row()
	{
		static const char *dummy_nil = "";

		if (ntoken_ <= 0 || buf_ptr_ == NULL || *buf_ptr_ == 0)
			return (NULL);

		hsrow* row;

		if (!rows_cache_.empty())
		{
			row = rows_cache_[rows_cache_.size() - 1];
			rows_cache_.pop_back();
			row->reset(ntoken_);
		}
		else
			row = NEW hsrow(ntoken_);

		char* last;
		int  i;
		for (i = 0; i < ntoken_; i++)
		{
			if (*buf_ptr_ == '\0')
			{
				row->push_back(dummy_nil, 1);
				break;
			}
			if (*buf_ptr_ == '\t')
			{
				row->push_back(dummy_nil, 1);
				buf_ptr_++;
				continue;
			}

			last = buf_ptr_;
			buf_ptr_ = strchr(buf_ptr_, '\t');
			if (buf_ptr_ == NULL)
			{
				row->push_back(last, strlen(last));
				break;
			}

			*buf_ptr_++ = 0;

			row->push_back(last, strlen(last));
		}

		if (debugOn_)
		{
			std::vector<const char*>::const_iterator cit =
				row->get_row().begin();
			for (; cit != row->get_row().end(); ++cit)
			{
				printf(">>>%s\n", *cit);
			}
		}

		return (row);
	}

	const std::vector<hsrow*>& hsproto::get()
	{
		while (true)
		{
			hsrow* row = get_next_row();
			if (row == NULL)
				break;
			rows_.push_back(row);
		}
		return (rows_);
	}

	void hsproto::reset()
	{
		std::vector<hsrow*>::iterator it = rows_.begin();
		for (; it != rows_.end(); ++it)
		{
			if (cache_enable_)
				rows_cache_.push_back(*it);
			else
				delete (*it);
		}
		rows_.clear();
	}

	void hsproto::clear_cache()
	{
		std::vector<hsrow*>::iterator it = rows_cache_.begin();
		for (; it != rows_cache_.end(); ++it)
			delete (*it);
		rows_cache_.clear();
	}

}
